package com.franklin.ideaplugin.easytesting.registry;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.franklin.ideaplugin.easytesting.rpc.RegistryCenter;
import com.franklin.ideaplugin.easytesting.rpc.entity.RegistryData;
import com.franklin.ideaplugin.easytesting.thread.EasyTestingThreadPool;
import com.google.common.collect.Lists;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * @author Ye Junhui
 * @since 2023/7/28
 */
public class ScheduleFileRegistry {

    private final Project project;

    private final String servicePath;

    public ScheduleFileRegistry(Project project) {
        this.project = project;
        this.servicePath = RegistryCenter.getProjectRegistryPath(project);
        this.startExecutor();
    }

    public static ScheduleFileRegistry getInstance(Project project) {
        return ServiceManager.getService(project, ScheduleFileRegistry.class);
    }

    private final ConcurrentMap<String, List<RegistryData>> serviceMap = new ConcurrentHashMap<>();

    private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(
            runnable -> {
                Thread thread = new Thread(runnable, "easy-testing-file-registry");
                thread.setDaemon(true);
                return thread;
            }
    );

    /**
     * 业务线程池
     */
    private final ThreadPoolExecutor workPool = new ThreadPoolExecutor(
            1,
            2,
            60L,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(2000),
            r -> {
                Thread thread = new Thread(r, "easy-testing-file-registry-work-thread-" + r.hashCode());
                thread.setDaemon(true);
                return thread;
            },
            (r, executor) -> {
                throw new RuntimeException("easy-testing, file registry work pool is EXHAUSTED!");
            });

    private void startExecutor() {
        this.executor.scheduleAtFixedRate(
                () -> {
                    File registryDirectory = FileUtil.file(servicePath);
                    if (!registryDirectory.isDirectory()) {
                        return;
                    }
                    File[] listFiles = registryDirectory.listFiles();
                    List<File> fileList = Lists.newArrayList(listFiles);
                    for (List<File> files : Lists.partition(fileList, 20)) {
                        workPool.execute(() -> {
                            files.stream()
                                    .filter(File::isDirectory)
                                    .forEach(file -> {
                                        String registryPath = file.getAbsolutePath();
                                        if (StrUtil.isBlank(registryPath)) {
                                            return;
                                        }
                                        List<RegistryData> registryDataList = getRegistryData(registryPath);
                                        serviceMap.put(registryPath, registryDataList);
                                    });
                        });
                    }
                },
                2, 3,
                TimeUnit.SECONDS
        );
    }

    @NotNull
    private static List<RegistryData> getRegistryData(String registryPath) {
        File registryDirectory = FileUtil.file(registryPath);
        if (!registryDirectory.isDirectory()) {
            return Collections.emptyList();
        }
        return Arrays.stream(registryDirectory.listFiles())
                .filter(file -> file.getName().startsWith("etServer_"))
                .map(file -> {
                    String name = file.getName();
                    String[] arr = name.split("_");
                    String appName = arr[1];
                    String port = FileUtil.readString(file.getAbsolutePath(), StandardCharsets.UTF_8);
                    return new RegistryData(appName, port);
                })
                .collect(Collectors.toList());
    }

    /**
     * 先取缓存后读文件
     *
     * @param project
     * @param registryPath
     * @return
     */
    public static List<RegistryData> getRegistryData(Project project, String registryPath) {
        ScheduleFileRegistry instance = getInstance(project);
        List<RegistryData> registryDataList = instance.serviceMap.get(registryPath);
        if (CollectionUtil.isNotEmpty(registryDataList)) {
            return registryDataList;
        }
        return getRegistryData(registryPath);
    }

}
